﻿using Common.Infrastructure.Structs;
using Consul;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Common.Infrastructure.Service
{
    public class AddressService : IAddressService
    {
        private static ConsulClient consulClient;
        private static AddressServiceByConsul consul = new AddressServiceByConsul();
        private readonly IConfiguration configuration;
        private readonly IServiceProvider provider = null;
        private Dictionary<string, string> address = new Dictionary<string, string>();
        private string apiKey = null;

        public AddressService(IServiceProvider provider, IConfiguration configuration)
        {
            this.provider = provider;
            this.configuration = configuration;
            apiKey = configuration["ApiKey"];
        }

        public static void InitServiceCode(string apikey, string initCodes)
        {
            if (string.IsNullOrEmpty(initCodes))
            {
                return;
            }

            initCodes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ForEach(t =>
            {
                var st = (string.IsNullOrEmpty(apikey) ? string.Empty : (apikey + "_")) + t;
                consul.Items[st] = new AddressServiceByConsulItem();
            });
        }

        public static void Start(string url, string http)
        {
            Task.Run(() =>
            {
                while (true)
                {
                    try
                    {
                        foreach (var s in consul.Items)
                        {
                            try
                            {
                                GetConsulItem(url, http, s.Key, s.Value);
                            }
                            catch 
                            {
                            }
                        }
                    }
                    catch 
                    { 
                    }

                    Thread.Sleep(10 * 1000);
                }
            });
        }

        public void ClearServiceAddress()
        {
            this.address.Clear();
        }

        public string GetServiceAddress(string serviceCode)
        {
            if (string.IsNullOrEmpty(serviceCode))
            {
                throw new ArgumentNullException($"param: serviceCode null");
            }

            serviceCode = (string.IsNullOrEmpty(apiKey) ? string.Empty : (apiKey + "_")) + serviceCode;
            if (!this.address.TryGetValue(serviceCode, out string v))
            {
                if (!consul.Items.TryGetValue(serviceCode, out AddressServiceByConsulItem item))
                {
                    item = new AddressServiceByConsulItem();
                    lock (consul)
                    {
                        consul.Items[serviceCode] = item;
                    }
                }

                if (item.LastGetTime == null ||
                    item.LastGetTime.Value.AddMinutes(10) < DateTime.Now ||
                    !item.Addresss.Any())
                {
                    var address = this.configuration["Consul:Address"];
                    #if DEBUG
                    address = "http://localhost:8500/"; 
                    #endif
                    if (string.IsNullOrEmpty(address))
                    {
                        throw new ArgumentNullException("Consul:Address");
                    }

                    GetConsulItem(address, this.configuration["Consul:Http"], serviceCode, item);
                    item.LastGetTime = DateTime.Now;
                }

                if (item != null && item.Addresss.Any())
                {
                    try
                    {
                        if (item.Index > item.Addresss.Count - 1)
                        {
                            item.Index = 0;
                        }

                        var itemAddress = item.Addresss[item.Index];
                        v = $"{itemAddress.Http}://{itemAddress.Ip}:{itemAddress.Port}";
                        if (!this.address.ContainsKey(serviceCode))
                        {
                            lock (address)
                            {
                                this.address[serviceCode] = v;
                            }
                        }

                        item.Index++;
                    }
                    catch 
                    {
                    }
                }
            }

            if (string.IsNullOrEmpty(v))
            {
                throw new AddressException($"The Service ${serviceCode} isn't exist");
            }

            return v;
        } 

        private static void GetConsulItem(string url, string http, string moduleCode, AddressServiceByConsulItem item)
        {
            if (consulClient == null)
            {
                consulClient = new ConsulClient(options =>
                {
                    options.Address = new Uri(url);
                });
            }

            var result = consulClient.Health.Service(moduleCode, null, true, item.Options).Result;
            item.Options.WaitIndex = result.LastIndex;
            var httpModel = !string.IsNullOrEmpty(http) ? http : "http";
            item.Addresss.Clear();
            foreach (var t in result.Response) 
            {
                item.Addresss.Add(new AddressServiceByConsulItemAddress()
                {
                    Http = httpModel,
                    Ip = t.Service.Address,
                    Port = t.Service.Port
                });
            }
        }
    }
}
